package com.sunlands.qdcy.tongzhuo.service;

import com.google.common.collect.Lists;
import com.sunlands.qdcy.tongzhuo.constant.RedisKeyConst;
import com.sunlands.qdcy.tongzhuo.exception.CheckRunException;
import com.sunlands.qdcy.tongzhuo.feign.vo.UserInfoDTO;
import com.sunlands.qdcy.tongzhuo.model.UserDiaryZanDO;
import com.sunlands.qdcy.tongzhuo.repository.UserDiaryZanRepository;
import com.sunlands.qdcy.tongzhuo.constant.CommonResultMessage;
import com.sunlands.qdcy.tongzhuo.vo.Page;
import com.sunlands.qdcy.tongzhuo.vo.UserDiaryZanVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.sunlands.qdcy.tongzhuo.config.RedisConfig.CacheKeys.DIARY_DETAIL_BY_ID;


/**
 * @author gaoyanfeng
 * @date 2019/5/2914:35
 */
@Service
@Slf4j
public class UserDiaryZanService extends BaseService {

    @Autowired
    private UserDiaryZanRepository userDiaryZanRepository;
    @Autowired
    private DiaryMessageService diaryMessageService;

    @Caching(evict = {
            @CacheEvict(cacheNames = DIARY_DETAIL_BY_ID, key = "#userDiaryZan.targetId", condition = "#userDiaryZan.targetId != null and #userDiaryZan.type == 1")
    })
    public void addUserDiaryZan(UserDiaryZanDO userDiaryZan, String userName) {
        userDiaryZanRepository.save(userDiaryZan);
        deleteCacheKey(userDiaryZan.getTargetId(), userDiaryZan.getType());
        String cancelZanCount = RedisKeyConst.getCancelZanCount(userDiaryZan.getTargetId(), userDiaryZan.getType(), userDiaryZan.getZanUserId());
        Object o = redisTemplate.opsForValue().get(cancelZanCount);
        if (null != o && (Integer) o > 5) {
            log.warn(userName + "点赞取消次数大于五次");
            return;
        }
        try {
            if (UserDiaryZanDO.TypeEnums.DIARY == UserDiaryZanDO.TypeEnums.create(userDiaryZan.getType())) {
                diaryMessageService.interactZanDiary(userDiaryZan.getZanUserId(), userDiaryZan.getZanedUserId());
            } else if (UserDiaryZanDO.TypeEnums.REPLY == UserDiaryZanDO.TypeEnums.create(userDiaryZan.getType())) {
                diaryMessageService.interactZanComment(userDiaryZan.getZanUserId(), userDiaryZan.getZanedUserId());
            }
        } catch (Exception e) {
            log.error("点赞调用消息系统异常 e={}", e.getStackTrace());
            sendExceptionUtil.sendException("点赞调用消息系统异常" + e.getMessage(), e);
        }
    }

    public UserDiaryZanDO getByDiaryIdAndUserId(Long targetId, Long userId) {
        return getByTargetIdAndTypeAndUserId(targetId, UserDiaryZanDO.TypeEnums.DIARY.code, userId);
    }

    public Page<UserDiaryZanVO> getByTargetIdAndType(Long targetId, Integer type, Integer page, Integer size) {
        Specification<UserDiaryZanDO> specification = new Specification<UserDiaryZanDO>() {
            @Override
            public Predicate toPredicate(Root<UserDiaryZanDO> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicateList = Lists.newArrayList();
                predicateList.add(criteriaBuilder.equal(root.get("targetId").as(Long.class), targetId));
                predicateList.add(criteriaBuilder.equal(root.get("type").as(Long.class), type));
                return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
            }
        };
        Sort sort = new Sort(Sort.Direction.DESC, Lists.newArrayList("createTime"));
        PageRequest pageRequest = new PageRequest(page, size, sort).previous();
        org.springframework.data.domain.Page pageResult = userDiaryZanRepository.findAll(specification, pageRequest);
        List<UserDiaryZanDO> userDiaryZans = pageResult.getContent();
        if (null == userDiaryZans || userDiaryZans.size() == 0) {
            return null;
        }
        List<UserDiaryZanVO> userDiaryZanVOList = Lists.newArrayList();
        for (UserDiaryZanDO userDiaryZan : userDiaryZans) {
            UserDiaryZanVO userDiaryZanVO = UserDiaryZanVO.from(userDiaryZan);
            userDiaryZanVOList.add(userDiaryZanVO);
            UserInfoDTO userById = getUserById(userDiaryZan.getZanUserId());
            if (null == userById) {
                throw new CheckRunException(CommonResultMessage.USER_INVALID.message);
            }
            userDiaryZanVO.setLearnTargetName(userById.getCurrentLearnTargetName());
            userDiaryZanVO.setZanUserName(userById.getName());
            userDiaryZanVO.setImgUrls(userById.getAvatarUrl());
        }
        Page<UserDiaryZanVO> resultVO = new Page<>();
        resultVO.setRows(userDiaryZanVOList);
        resultVO.setTotal(pageResult.getTotalElements());
        resultVO.setPageNumber(page);
        return resultVO;
    }

    private void deleteCacheKey(Long targetId, Integer type) {
        String userDiaryZanCountKey = RedisKeyConst.getUserDiaryZanCount(targetId, type);
        redisTemplate.delete(userDiaryZanCountKey);
    }

    public Integer getZanCountByDiaryId(Long targetId, Integer type) {
        String userDiaryZanCountKey = RedisKeyConst.getUserDiaryZanCount(targetId, type);
        Object o = redisTemplate.opsForValue().get(userDiaryZanCountKey);
        if (o != null) {
            return (Integer) o;
        }
        Long count = userDiaryZanRepository.countByTargetIdAndType(targetId, type);
        redisTemplate.opsForValue().set(userDiaryZanCountKey, count.intValue(), 300, TimeUnit.SECONDS);
        return count.intValue();
    }

    @Caching(
            evict = {
                    @CacheEvict(cacheNames = DIARY_DETAIL_BY_ID, key = "#targetId", condition = "#type ==1")
            }
    )
    public void deleteZanById(Long id, Long targetId, Integer type, Long userId) {
        userDiaryZanRepository.delete(id);
        String targetTypeZanByUserid = RedisKeyConst.getTargetTypeZanByUserid(targetId, type, userId);
        redisTemplate.delete(targetTypeZanByUserid);
        deleteCacheKey(targetId, type);
        String key = RedisKeyConst.getCancelZanCount(targetId, type, userId);
        if (null == key) {
            redisTemplate.opsForValue().set(key, 1, 300, TimeUnit.SECONDS);
        } else {
            redisTemplate.opsForValue().increment(key, 1);
        }
    }

    public UserDiaryZanDO getByTargetIdAndTypeAndUserId(Long targetId, Integer type, Long userId) {
        String targetTypeZanByUserid = RedisKeyConst.getTargetTypeZanByUserid(targetId, type, userId);
        Object o = redisTemplate.opsForValue().get(targetTypeZanByUserid);
        if (null == o) {
            UserDiaryZanDO byTargetIdAndTypeAndZanUserId = userDiaryZanRepository.findByTargetIdAndTypeAndZanUserId(targetId, type, userId);
            if (null != byTargetIdAndTypeAndZanUserId) {
                redisTemplate.opsForValue().set(targetTypeZanByUserid, byTargetIdAndTypeAndZanUserId, 300, TimeUnit.SECONDS);
            }
            return byTargetIdAndTypeAndZanUserId;
        }
        return (UserDiaryZanDO) o;
    }

    public List<Long> ifDiaryZanByUserId(List<Long> diaryIdList, Long userId) {
        Iterator<Long> iterator = diaryIdList.iterator();
        while (iterator.hasNext()) {
            Long next = iterator.next();
            UserDiaryZanDO byDiaryIdAndUserId = getByDiaryIdAndUserId(next, userId);
            if (null == byDiaryIdAndUserId) {
                iterator.remove();
            }
        }
        return diaryIdList;
    }

    public org.springframework.data.domain.Page findAll(Specification<UserDiaryZanDO> specification, PageRequest pageRequest) {
        return userDiaryZanRepository.findAll(specification, pageRequest);
    }
}
